package com.zmn.oms.manager.utils;

import com.zmn.manager.redis.RedisManager;
import com.zmn.oms.common.exception.OmsBaseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.Closeable;

/**
 * 类描述：锁工具，用于避免同一时间的重复操作
 *
 * @author LiangHailong
 * @date 2021/01/09 16:59
 */
public final class LockUtil {
    private static RedisManager redisManager;
    private static final int defaultSecondsTimeout = 60 * 5;    // 默认过期时间：5分钟

    /**
     * 获取一个有效时间为5分钟的锁
     * @param key
     * @return
     * @throws OmsBaseException
     */
    public static Lock getLock(String key) throws OmsBaseException {
        return getLock(key, defaultSecondsTimeout);
    }

    /**
     * 获取一个指定有效时间的锁
     * @param key
     * @param secondsTimeout
     * @return 当key已被另一个锁获取时，返回null
     * @throws OmsBaseException
     */
    public static Lock getLock(String key, int secondsTimeout) throws OmsBaseException {
        if (StringUtils.isBlank(key)) {
            throw new OmsBaseException("获取锁的key不能为空");
        }
        if (secondsTimeout <= 0) {
            throw new OmsBaseException("有效时间必须大于0");
        }
        RedisManager redisManagerBean = redisManager;
        if (redisManagerBean == null) {
            redisManagerBean = SpringBeanFactoryUtil.getApplicationContext().getBean(RedisManager.class);
            if (redisManagerBean == null) {
                throw new OmsBaseException("获取锁失败：系统异常");
            }
            redisManager = redisManagerBean;
        }

        Lock lock = null;
        try {
            lock = new Lock(redisManagerBean, key, secondsTimeout);
        } catch (RuntimeException e) {
        }

        return lock;
    }


    /**
     * 锁对象
     */
    @Slf4j
    public static final class Lock implements Closeable {
        private RedisManager redisManager;
        private String key;

        @Override
        public void close() {
            if (redisManager == null || key == null) {
                return;
            }
            try {
                redisManager.del(this.key);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }

        /**
         * 私有构造器，禁止外部实例化
         * @param key
         */
        private Lock(RedisManager redisManager, String key, int secondsTimeout) throws OmsBaseException {
            this.redisManager = redisManager;
            this.key = key;

            try {
                Long num = redisManager.incr(key);
                if (num.intValue() > 1) {
                    throw new RuntimeException("已被其他人锁定");
                }
                // 设置自动过期，清除Redis占用
                redisManager.expire(key, secondsTimeout);
            } catch (Exception e) {
                if (e instanceof RuntimeException) {
                    throw e;
                }
                throw new OmsBaseException(String.format("获取锁失败：%s", e.getMessage()), e);
            }
        }
    }

    /**
     * 私有构造器，禁止外部实例化
     */
    private LockUtil() {}
}
